﻿using System;
using System.ComponentModel.Design;
using System.Diagnostics;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.TestTools.Common;
using Microsoft.VisualStudio.TestTools.Vsip;

namespace NUnitForVSTS.NUnitTest
{
    
    /// <summary>
    /// Provider for the different test services
    /// </summary>
	public class ServiceProvider : IServiceProvider
	{
		readonly IServiceProvider parent;

        /// <summary>
        /// Test managener. Responsible for the test infrastructure
        /// </summary>
		ITestManagement _testManagement;
        /// <summary>
        /// Test Tip. Responsible for the loading of the tests
        /// </summary>
		NUnitTestTip _tip;

        /// <summary>
        /// Initializes a new instance of the <see cref="ServiceProvider"/> class.
        /// </summary>
        /// <param name="parent">The parent.</param>
		public ServiceProvider(IServiceProvider parent)
		{
			this.parent = parent;

			IServiceContainer container = GetService(typeof(IServiceContainer)) as IServiceContainer;
            Debug.Assert(container != null, "Packagee didn't provide IServiceContainer so we cannot provide services");
            container.AddService(typeof(ITestService), OnCreateService, true);
		}

        /// <summary>
        /// Called when [create service].
        /// </summary>
        /// <param name="container">The container.</param>
        /// <param name="serviceType">Type of the service.</param>
        /// <returns></returns>
		private object OnCreateService(IServiceContainer container, Type serviceType)
		{
			Check.NotNull(container, "container");
			Check.NotNull(serviceType, "serviceType");

            if (serviceType == typeof(ITestService))
            {
                return new NUnitTestTuip(this);
            }
			return null;
		}

        /// <summary>
        /// Gets the service object of the specified type.
        /// </summary>
        /// <param name="serviceType">An object that specifies the type of service object to get.</param>
        /// <returns>
        /// A service object of type <paramref name="serviceType"/>.
        /// -or-
        /// null if there is no service object of type <paramref name="serviceType"/>.
        /// </returns>
		public object GetService(Type serviceType)
		{
			return parent.GetService(serviceType);
		}

        /// <summary>
        /// Gets the service.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="serviceType">Type of the service.</param>
        /// <returns></returns>
		public T GetService<T>(Type serviceType) where T : class
		{
			return GetService(serviceType) as T;
		}

        /// <summary>
        /// Gets the test management controller
        /// </summary>
        /// <value>The test management.</value>
		public ITestManagement TestManagement
		{
			get
			{
				if (_testManagement == null)
					_testManagement = GetService<ITestManagement>(typeof(STestManagement));
				return _testManagement;
			}
		}

        /// <summary>
        /// Gets the Test Metadata Interface (TMI)
        /// </summary>
        /// <value>The tmi.</value>
		public ITmi Tmi
		{
			get { return TestManagement.TmiInstance; }
		}

        /// <summary>
        /// Gets the test interface provider (TIP).
        /// </summary>
        /// <value>The tip.</value>
		public NUnitTestTip Tip
		{
			get
			{
                if (_tip == null)
                {
                    _tip = Tmi.FindTipForTestType(UnitTest.NUnitTestType) as NUnitTestTip;
                }
				return _tip;
			}
		}
	}
}
